home *** CD-ROM | disk | FTP | other *** search
-
- /*
- File: DecompressShape.c
-
- Contains: graphics libraries - shape decompression
-
- Written by: Mike Reed
-
- Copyright: © 1995 by Apple Computer, Inc., all rights reserved.
-
- Writers:
-
- (jtd) John Daggett
-
- Change History (most recent first):
-
- <2> 9/14/95 jtd replaced boolean with Boolean
- <1> 9/14/95 jtd First checked in.
- */
-
- #include <Drag.h>
- #include <Gestalt.h>
- #include <ImageCompression.h>
- #include <Memory.h>
- #include <Scrap.h>
-
- #include <GXTypes.h>
- #include <GXMath.h>
- #include <GXGraphics.h>
- #include <GXEnvironment.h>
- #include "StorageLibrary.h"
- #include "DecompressShape.h"
-
- #define LONGALIGN(n) (((n) + 3) & ~3L)
- #define kAtomHeaderSize (sizeof(Size) + sizeof(OSType))
-
- static void RectangleToRect(const gxRectangle* gxr, Rect* qdr)
- {
- qdr->left = FixedRound(gxr->left);
- qdr->top = FixedRound(gxr->top);
- qdr->right = FixedRound(gxr->right);
- qdr->bottom = FixedRound(gxr->bottom);
- }
-
- static long* AppendAtom(long stream[], Size size, OSType tag, const void* data)
- {
- #ifdef debugging
- if (size & 3)
- DebugStr("\patom size needs to be long aligned");
- #endif
-
- *stream++ = size + kAtomHeaderSize;
- *stream++ = tag;
- BlockMove(data, (Ptr)stream, size);
-
- return (long*)((char*)stream + size);
- }
-
- /*
- * See the comment on DecompressShape for an explaination of the parameters.
- * This routine is used by both DecompressShape for embedding shapes in PICTs,
- * and AddQDGXRecorderFrame for making gx movies.
- */
- Handle CreateQDGXStream(gxShape source, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground)
- {
- #define gxForPrintingOnlyAtom 'fpto'
- #define gxEraseBackgroundAtom 'erbg'
-
- long atomCount, shapeSize, proxieSize, dataSize, fontListSize, eraseSize;
- Handle dataHdl, shapeHdl;
- gxFlatFontList* fontList;
- gxTag fontListTag;
-
- #ifdef debugging
- GXIgnoreGraphicsWarning(tags_of_type_flst_removed);
- #endif
- shapeHdl = ShapeToHandleWithFlags(source, gxFontListFlatten | gxFontGlyphsFlatten | gxFontVariationsFlatten);
- #ifdef debugging
- GXPopGraphicsWarning();
- #endif
- if (shapeHdl == nil)
- return nil;
-
- if (proxie)
- { atomCount = 2;
- proxieSize = LONGALIGN(GetHandleSize((Handle)proxie));
- }
- else
- { atomCount = 1;
- proxieSize = 0;
- }
- shapeSize = LONGALIGN(GetHandleSize(shapeHdl));
-
- if (forPrintingOnly)
- ++atomCount;
- if (eraseBackground)
- ++atomCount;
-
- fontListSize = 0;
- fontList = nil;
- GXIgnoreGraphicsWarning(count_out_of_range);
- if (GXGetShapeTags(source, gxFlatFontListItemTag, 1, 1, &fontListTag) > 0)
- { fontListSize = GXGetTag(fontListTag, nil, nil);
- if (fontListSize > 0)
- { fontList = (gxFlatFontList*)NewPtr(fontListSize);
- if (fontList != nil)
- { GXGetTag(fontListTag, nil, fontList);
- fontListSize = LONGALIGN(fontListSize);
- ++atomCount;
- }
- else
- fontListSize = 0;
- }
- }
- GXPopGraphicsWarning(); // count_out_of_range
-
- dataSize = atomCount * kAtomHeaderSize + shapeSize + proxieSize + fontListSize + sizeof(long);
- dataHdl = NewHandle(dataSize);
- if (dataHdl == nil)
- { DisposHandle(shapeHdl);
- if (fontList)
- DisposPtr((Ptr)fontList);
- return nil;
- }
-
- { long* p = (long*)*dataHdl;
-
- if (forPrintingOnly)
- p = AppendAtom(p, 0, gxForPrintingOnlyAtom, nil);
- if (eraseBackground)
- p = AppendAtom(p, 0, gxEraseBackgroundAtom, nil);
- if (proxie)
- p = AppendAtom(p, proxieSize, 'PICT', *proxie);
- if (fontList)
- p = AppendAtom(p, fontListSize, gxFlatFontListItemTag, fontList);
- p = AppendAtom(p, shapeSize, 'qdgx', *shapeHdl);
- *p++ = 0; // end of the atom-list
-
- DisposHandle(shapeHdl);
- if (fontList)
- DisposPtr((Ptr)fontList);
- }
- return dataHdl;
- }
-
- static void GetRidOfAnyQDShapeTags(gxShape shape)
- {
- gxShapeType shapeType = GXGetShapeType(shape);
-
- if (shapeType == gxPictureType)
- { long index, count;
- gxShape* subShapes;
-
- count = GXGetPicture(shape, nil, nil, nil, nil);
- if (count > 0)
- { subShapes = (gxShape*)NewPtr(count * sizeof(gxShape));
- if (subShapes != nil)
- { GXGetPicture(shape, subShapes, nil, nil, nil);
- for (index = 0; index < count; index++)
- GetRidOfAnyQDShapeTags(subShapes[index]);
- DisposPtr((Ptr)subShapes);
- }
- }
- }
- else if (shapeType == gxRectangleType && GXGetShapeTags(shape, gxQuickDrawPictTag, 1, gxSelectToEnd, nil) > 0)
- GXSetShapeType(shape, gxPictureType);
- }
-
- /*
- * This guy returns a Quickdraw picture containing an embedded shape, and a proxie
- * of the shape, if proxie is not nil. This is called by ShapeToScrap and DragAndDropShape.
- *
- * theShape • the shape you want to embedd in a PICT
- * proxie • a PICT to be drawn if theShape cannot be drawn (optional but recommended)
- * forPrintingOnly • if TRUE, then the decompressor will always look for the proxie
- * and theShape will only be used when printing. Use this setting if
- * theShape might be too large or too slow when drawn from other apps.
- * • If FALSE, then the decompressor will draw theShape unless it
- * gets an error, in which case it will look for a proxie.
- * eraseBackground • if TRUE, the decompressor will always erase the background to WHITE
- * before drawing the shape. This is slower, but needed if the shape does not
- * fill its bounding rectangle.
- * • if FALSE, the decompressor will just draw the shape. Use this setting
- * if the shape entirely fills its bounding rectangle.
- *
- * The shape [and proxie] is embedded by constructing a stream of atoms. Each atom begins
- * with a size (long) and a type (OSType) and then the data for that type. After the last atom,
- * there is a trailing zero (long) to mark the end of the stream. For embedded shapes, the type
- * is 'qdgx', and for the proxie the type is 'PICT'. Note that the size fields are rounded up to
- * a multiple of 4. Finally, to alert QuickTime that the data is in this parsable form with a
- * possible PICT proxie, we add a 'prxy' extension to the ImageDescriptionHandle.
- *
- * Picture of this form will draw the embedded shape when an application calls DrawPicture
- * if GX is around, and if not, the proxie will be drawn. When printed, the shape or the proxie
- * will be printed. This is meant to replace the PicComment described in GX 1.0 for embedding
- * shapes in pictures.
- *
- * If you want to include a flatFontList tag, be sure that theShape is a picture, otherwise GX will
- * not return the tag after GXFlattenShape. The flatFontList tag makes certain printing conditions
- * more efficient (i.e. font downloading to postscript printers).
- *
- * Your shape must not contain a gxQuickDrawPictTag, meaning it contains embedded QD data, becuase
- * this will potentially crash when it tries to print. To fix that, DecompressShape looks for occurrances
- * of the tag, and converts them to real gx data by calling GXSetShapeType(shape, gxPictureType).
- */
- PicHandle DecompressShape(gxShape theShape, PicHandle proxie, Boolean forPrintingOnly, Boolean eraseBackground)
- {
- #define kQuickTimeGestalt 'qtim'
-
- PicHandle thePicture;
- ImageDescriptionHandle descHdl;
- ImageDescriptionPtr descPtr;
- Handle dataHdl;
- long version;
-
- if (Gestalt(kQuickTimeGestalt, &version) != noErr)
- return nil;
-
- GetRidOfAnyQDShapeTags(theShape);
-
- /*
- * Move the shape's topLeft to 0,0 so that it draws neatly inside the picture frame.
- * Note that the qdgx movie library does not move the shape, since the shape may not
- * take up the whole frame.
- */
- { gxRectangle bounds;
-
- GXGetShapeLocalBounds(theShape, &bounds);
- if (bounds.left || bounds.top)
- GXMoveShape(theShape, -bounds.left, -bounds.top);
- dataHdl = CreateQDGXStream(theShape, proxie, forPrintingOnly, eraseBackground);
- if (bounds.left || bounds.top)
- GXMoveShape(theShape, bounds.left, bounds.top);
- }
- if (dataHdl == nil)
- return nil;
-
- descHdl = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
- if (descHdl)
- { Rect shortBounds;
- gxRectangle bounds;
-
- GXGetShapeLocalBounds(theShape, &bounds);
- RectangleToRect(&bounds, &shortBounds);
- OffsetRect(&shortBounds, -shortBounds.left, -shortBounds.top); // set the topLeft of the src to 0,0
- thePicture = OpenPicture(&shortBounds);
-
- descPtr = *descHdl;
- descPtr->idSize = sizeof(ImageDescription);
- descPtr->cType = 'qdgx';
- descPtr->vendor = 'appl';
- descPtr->temporalQuality = codecLosslessQuality;
- descPtr->width = shortBounds.right;
- descPtr->height = shortBounds.bottom;
- descPtr->hRes = descPtr->vRes = ff(72);
- descPtr->dataSize = GetHandleSize(dataHdl);
- descPtr->frameCount = 1;
- descPtr->depth = 32;
- descPtr->clutID = -1;
-
- // If there is a PICT proxie, add an image extension to tell QuickTime, in case GX is not around.
- if (proxie)
- { Handle prxyVersionHdl = NewHandle(sizeof(long));
-
- if (prxyVersionHdl != nil)
- { *(long*)*prxyVersionHdl = 0; // version number for 'prxy' extension
- SetImageDescriptionExtension(descHdl, prxyVersionHdl, 'prxy');
- }
- }
-
- HLock(dataHdl);
- DecompressImage(*dataHdl, descHdl, ((CGrafPtr)qd.thePort)->portPixMap, &shortBounds, &shortBounds, srcCopy, nil);
- DisposeHandle((Handle)descHdl);
- ClosePicture();
- }
- else
- thePicture = nil;
-
- DisposHandle(dataHdl);
-
- return thePicture;
- }
-
- /*
- * This guy returns a Quickdraw picture containing a 1-bit bitmap of the shape.
- * This is used by ShapeToScrap to create a proxie when calling DecompressShape.
- * If you want to make the proxie prettier (and larger), change the bitmap to 8-bit.
- * However, if you're using this in conjunction with DecompressShape to place a
- * gxShape on the clipboard, 1-bit should be enough, since the actual shape will be
- * drawn, rather than the proxie (unless forPrintingOnly is true).
- */
- PicHandle ShapeToPICT(gxShape source)
- {
- gxRectangle bounds;
- gxShape bitShape;
- gxBitmap bitmap;
- PicHandle thePicture;
- Rect shortBounds;
-
- /*
- * GetShapeLocalBounds doesn't accurately report the bounds of a gxQuickDrawPictTag.
- * One option is to convert the tags to real GX pictures.
- */
- GetRidOfAnyQDShapeTags(source);
-
- GXGetShapeLocalBounds(source, &bounds);
- RectangleToRect(&bounds, &shortBounds);
- OffsetRect(&shortBounds, -shortBounds.left, -shortBounds.top);
-
- bitmap.width = shortBounds.right;
- bitmap.height = shortBounds.bottom;
- bitmap.rowBytes = bitmap.width + 31 >> 5 << 2;
- bitmap.pixelSize = 1;
- bitmap.space = gxIndexedSpace;
- bitmap.set = nil;
- bitmap.profile = nil;
- bitmap.image = NewPtrClear(bitmap.rowBytes * bitmap.height);
- if (bitmap.image == nil)
- return nil;
-
- bitShape = GXNewBitmap(&bitmap, nil);
- if (bitShape != nil)
- { gxViewGroup group = GXNewViewGroup();
- gxViewDevice device = GXNewViewDevice(group, bitShape);
- gxViewPort port = GXNewViewPort(group);
- gxTransform trans = GXCloneTransform(GXGetShapeTransform(source));
-
- GXSetShapeAttributes(source, GXGetShapeAttributes(source) | gxMapTransformShape);
- GXMoveShape(source, -bounds.left, -bounds.top);
- GXSetViewPortDither(port, 4);
- GXSetShapeViewPorts(source, 1, &port);
- GXDrawShape(source);
- GXSetShapeTransform(source, trans);
- GXDisposeTransform(trans);
-
- GXDisposeViewGroup(group); /* this disposes the gxViewPort and gxViewDevice */
- GXDisposeShape(bitShape);
- }
-
- { GrafPtr thePort;
- BitMap srcBits;
-
- GetPort(&thePort);
- srcBits.baseAddr = bitmap.image;
- srcBits.rowBytes = bitmap.rowBytes;
- srcBits.bounds = shortBounds;
-
- thePicture = OpenPicture(&shortBounds);
- CopyBits(&srcBits, &thePort->portBits, &shortBounds, &shortBounds, srcOr, nil);
- ClosePicture();
- }
-
- DisposPtr((Ptr)bitmap.image);
-
- return thePicture;
- }
-
- /*
- * This guy puts a Quickdraw picture on the clipboard containing an embedded shape
- * and, if addProxie is true, a 1-bit bitmap of the shape. Call this in response to
- * the user choosing "Copy" or "Cut" from the Edit menu. See comment for DecompressShape
- * to explain forPrintingOnly.
- */
- void ShapeToScrap(gxShape source, Boolean addProxie, Boolean forPrintingOnly, Boolean eraseBackground)
- {
- PicHandle picture, proxie;
-
- proxie = addProxie ? ShapeToPICT(source) : nil;
- picture = DecompressShape(source, proxie, forPrintingOnly, eraseBackground);
- if (proxie)
- KillPicture(proxie);
- if (picture)
- { HLock((Handle)picture);
- ZeroScrap();
- PutScrap(GetHandleSize((Handle)picture), 'PICT', (Ptr)*picture);
- KillPicture(picture);
- }
- }
-
- /*
- * The ItemReference is the gxShape to be sent. The dragSendRefCon is ignored.
- */
- static pascal OSErr LibrarySendDataProc(FlavorType theType, void *dragSendRefCon,
- ItemReference theItem, DragReference theDrag)
- {
- OSErr result = noErr;
- gxShape shape = (gxShape)theItem;
-
- switch (theType) {
- case 'qdgx':
- { Handle flat = ShapeToHandle(shape);
-
- if (flat)
- { HLock(flat);
- result = SetDragItemFlavorData(theDrag, theItem, 'qdgx', *flat, GetHandleSize(flat), 0);
- DisposHandle(flat);
- }
- break;
- }
- case 'PICT':
- { PicHandle proxie = ShapeToPICT(shape);
- PicHandle pict = DecompressShape(shape, proxie, false, true);
-
- if (proxie)
- KillPicture(proxie);
- if (pict)
- { HLock((Handle)pict);
- result = SetDragItemFlavorData(theDrag, theItem, 'PICT', (Ptr)*pict, GetHandleSize((Handle)pict), 0);
- KillPicture(pict);
- }
- break;
- }
- default:
- result = badDragFlavorErr;
- break;
- }
- return result;
- }
-
- /*
- * This guy will take the current mouse-down event and a shape, and handle dragging it to the desktop
- * or to another Drag-friendly application. It offers to deliver the shape as either 'qdgx' or as a 'PICT'
- * with the shape embedded using DecompressShape. To offer other flavors, just add additional calls
- * to AddDragItemFlavor below, and add the corresponding cases in the switch statement in LibrarySendDataProc.
- */
- void DragAndDropShape(EventRecord* event, gxShape shape)
- {
- RgnHandle hilightRgn;
- gxRectangle bounds;
- Rect r;
- DragReference theDrag;
-
- if (NewDrag(&theDrag) == noErr)
- { SetDragSendProc(theDrag, LibrarySendDataProc, nil);
- AddDragItemFlavor(theDrag, (unsigned long)shape, 'qdgx', nil, 0, 0);
- AddDragItemFlavor(theDrag, (unsigned long)shape, 'PICT', nil, 0, 0);
-
- hilightRgn = NewRgn();
- GXGetShapeDeviceBounds(shape, 0, 0, &bounds);
- RectangleToRect(&bounds, &r);
- RectRgn(hilightRgn, &r);
- SetDragItemBounds(theDrag, 1, &r);
-
- // turn the region from a fill into a frame
- { RgnHandle tempRgn = NewRgn();
-
- CopyRgn(hilightRgn, tempRgn);
- InsetRgn(tempRgn, 1, 1);
- DiffRgn(hilightRgn, tempRgn, hilightRgn);
- DisposeRgn(tempRgn);
- }
-
- TrackDrag(theDrag, event, hilightRgn);
- DisposeDrag(theDrag);
- DisposeRgn(hilightRgn);
- }
- }
-
-